﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

#pragma once

#include "ApiBaseObject.h"
#include "ApiObjectCreatorMap.h"
#include "WeakPtr.h"
#include "GbmpNew.h"

namespace gdmp {
    namespace api {
        namespace traits
        {
            template <typename T>
            struct is_weak {
                template <typename U>
                static std::true_type test(decltype(U::m_wpInternal)*);

                template <typename U>
                static std::false_type test(...);

                static constexpr bool value = decltype(test<T>(nullptr))::value;
            };
        }

        class InternalObjectBase :public gdmp::api::BaseObject {};

        template<typename T>
        class InternalObject1 : public InternalObjectBase
        {
        public:
            virtual bool IsValidObject()const override { return nullptr != GetInternalObject(); }

            template<typename Derived>
            const Derived* GetInternalObject()const
            {
                const T* ptr = Get();
                return gcmp::quick_cast<Derived>(ptr);
            }
            template<typename Derived>
            Derived* GetInternalObject()
            {
                T* ptr = Get();
                return gcmp::quick_cast<Derived>(ptr);
            }
            virtual const T* GetInternalObject()const { return Get(); }
            virtual T* GetInternalObject() { return Get(); }

            template<typename Derived>
            void SetInternalObject(const Derived* ptr)
            {
                static_assert(std::is_base_of<::gcmp::WeakReferenceable, Derived>::value, "Template type must be derived from WeakReferenceable.");
                Set(ptr);
            }

            void SetInternalObject(T* ptr) { SetInternalObject<T>(ptr); }


            template<typename APIType, typename InternalType>
            static APIType* New(gcmp::OwnerPtr<InternalType>&& ownerptr)
            {
                if (!ownerptr) return nullptr;
                APIType* o = NewImpl<APIType>(ownerptr.get());
                if (nullptr == o)return nullptr;
                o->m_opInternal = TransferOwnership(ownerptr);
                return o;
            }

            template<typename APIType>
            static APIType* New(T* ptr)
            {
                if (!ptr)return nullptr;
                APIType* o = NewImpl<APIType>(ptr);
                if (nullptr == o)return nullptr;
                o->SetInternalObject(ptr);
                return o;
            }
            template<typename APIType, typename InternalType = T>
            static APIType* New(const InternalType* ptr)
            {
                static_assert(traits::is_weak<APIType>::value,"Please use New(gcmp::OwnerPtr<InternalType>&&) instead.");
                if (!ptr)return nullptr;
                APIType* o = NewImpl<APIType>(ptr);
                if (nullptr == o)return nullptr;
                o->SetInternalObject(ptr);
                return o;
            }

            template<typename APIType>
            static APIType* New(const T& value)
            {
                APIType* o = NewImpl<APIType>(&value);
                if (nullptr == o)return nullptr;
                o->m_opInternal = NEW_AS_OWNER_PTR(T, value);
                return o;
            }

        protected:
            virtual const T* Get()const { return  m_opInternal.get(); }
            virtual T* Get() { return m_opInternal.get(); }
            virtual void Set(const T* ptr) {}
            virtual void Set(T* ptr) {}

        private:
            template<typename APIType, typename InternalType>
            static APIType* NewImpl(InternalType* p)
            {
                return NewImpl<APIType, InternalType>(p, traits::has_object_kind<InternalType>::type{});
            }
            template<typename APIType, typename InternalType>
            static APIType* NewImpl(InternalType* p, std::true_type)
            {
                ObjectCreator creator = ObjectCreatorMap::get(p);
                if (nullptr != creator)
                    return gcmp::quick_cast<APIType>(creator());
                return NEW_AS_PTR(APIType);
            }
            template<typename APIType, typename InternalType>
            static APIType* NewImpl(InternalType*, std::false_type)
            {
                return NEW_AS_PTR(APIType);
            }
        protected:
            gcmp::OwnerPtr<T> m_opInternal;
        };

        template<typename T>
        class InternalObject2 : public InternalObject1<T>
        {
        protected:
            virtual const T* Get()const override final { return  m_wpInternal ? m_wpInternal.Get() : m_opInternal.get(); }
            virtual T* Get() override final { return m_wpInternal ? m_wpInternal.Get() : m_opInternal.get(); }
            virtual void Set(const T* ptr)override final { m_wpInternal.Reset(ptr); }
            virtual void Set(T* ptr) override final { m_wpInternal.Reset(ptr); }
        protected:
            gcmp::WeakPtr<T> m_wpInternal;
        };

        template<typename T>
        using InternalObject = typename std::conditional<std::is_base_of<::gcmp::WeakReferenceable, T>::value, InternalObject2<T>, InternalObject1<T>>::type;

        template<typename T, typename TBase, typename TAPIBase>
        class InternalWeakObject :public TAPIBase
        {
        public:
            virtual const T* GetInternalObject()const override final { return static_cast<const T*>(Get()); }
            virtual T* GetInternalObject() override final { return static_cast<T*>(Get()); }

        protected:
            const TBase* Get()const override final { return  m_wpInternal ? m_wpInternal.Get() : m_opInternal.get(); }
            TBase* Get() override final { return m_wpInternal ? m_wpInternal.Get() : m_opInternal.get(); }
            void Set(const TBase* ptr)override final { m_wpInternal.Reset(static_cast<const T*>(ptr)); }
            void Set(TBase* ptr) override final { m_wpInternal.Reset(static_cast<T*>(ptr)); }
        private:
            gcmp::WeakPtr<T> m_wpInternal;
        };
    }
}


#define __GET_MACRO__(_1, _2, N, ...) N
#define __AUGMENTER__(...) unused, __VA_ARGS__

/*
for example:
    class A{}
    class B:public B{}
    void ApiA:Func()
    {
        A* a=SAFE_GET_INTERNAL_OBJECT();
        B* b=SAFE_GET_INTERNAL_OBJECT(B);
    }
*/
#define SAFE_GET_INTERNAL_OBJECT(...) __SAFE_GET_INTERNAL_OBJECT__(__AUGMENTER__(__VA_ARGS__))(__VA_ARGS__)
#define __SAFE_GET_INTERNAL_OBJECT__(...) EXPAND(__GET_MACRO__(__VA_ARGS__, __SAFE_GET_INTERNAL_OBJECT_1__,__SAFE_GET_INTERNAL_OBJECT_0__))
#define __SAFE_GET_INTERNAL_OBJECT_0__() ([this]{auto p=this->GetInternalObject();VALIDATE_INTERNAL_OBJECT(p);return p;})()
#define __SAFE_GET_INTERNAL_OBJECT_1__(TYPE) ([this]{auto p=this->GetInternalObject<TYPE>();VALIDATE_INTERNAL_OBJECT(p);return p;})()

/*
for example:
    class A{}
    class B:public B{}
    void C:Func(ApiA* a)
    {
        A* a=SAFE_GET_INTERNAL_ARGUMENT(a);
        B* b=SAFE_GET_INTERNAL_ARGUMENT(B,a);
    }
*/
#define SAFE_GET_INTERNAL_ARGUMENT(...) EXPAND(__GET_MACRO__(__VA_ARGS__, __SAFE_GET_INTERNAL_OBJECT_ARGUMENT_2__,__SAFE_GET_INTERNAL_OBJECT_ARGUMENT_1__)(__VA_ARGS__))
#define __SAFE_GET_INTERNAL_OBJECT_ARGUMENT_1__(ARG)([&ARG]{VALIDATE_ARGUMENTNULL(ARG); auto p=ARG->GetInternalObject();VALIDATE_INTERNAL_ARGUMENT(p);return p;})()
#define __SAFE_GET_INTERNAL_OBJECT_ARGUMENT_2__(TYPE,ARG)([&ARG]{VALIDATE_ARGUMENTNULL(ARG); auto p=ARG->GetInternalObject<TYPE>();VALIDATE_INTERNAL_ARGUMENT(p);return p;})()
